/******************
 *  init_fat()
 *  inicializace prace se souborovym systemem FAT12
 */
int init_fat(void) {
  int fatsize;

  /* alokace mista pro strukturu Fat */
  _fat          = mem_alloc(sizeof(Fat), KERNEL_SEGMENT);
  _fat->changed = true;

  /* nacteme MBR */
  _fat->mbr     = mem_alloc(sizeof(BootRecord), KERNEL_SEGMENT);
  floppyread(1, 1, _fat->mbr);

  /* sektor korenove slozky "/" */
	_fat->root_dir_sector   = (((_fat->mbr->root_size * 32) +
                             (_fat->mbr->sector_size - 1)) /
                              _fat->mbr->sector_size);

  /* pocet sektoru na FAT */
	_fat->sectors           = _fat->mbr->fat_count * _fat->mbr->fat_size16;

	/* sektor, kde zacinaji data */
	_fat->data_start_sector = _fat->mbr->sector_reserved + _fat->sectors +
                               _fat->root_dir_sector + 1;

  /* nacteni FAT */
  fatsize            = _fat->mbr->sector_size * _fat->mbr->fat_size16;
  _fat->fat          = mem_alloc(fatsize, KERNEL_SEGMENT);
  fat_load();

  return 0;
}

/**
 *  fat_end()
 *  zjisti, jestli je <cluster> "posledni"
 *  IN:  <cluster>
 *  OUT: [bool] - true  - je posledni
 *                false - neni posledni
 */
bool fat_end(unsigned int cluster) {
  return (((cluster == FAT_BADSECTOR) || (cluster == FAT_AVAILABLE)
             || (cluster == FAT_INVALIDENTRY)) || ((cluster >= FAT_RESERVEDs)
             && (cluster <= FAT_RESERVEDe)) || ((cluster >= FAT_LASTCLUSTERs)
             && (cluster <= FAT_LASTCLUSTERe)));
}

/**
 *  fat_load()
 *  nacte FAT tabulku z disku do globalniho pole <fat>
 *  IN:   [void]
 *  OUT:  [void]
 */
void fat_load(void) {
  unsigned int SectorsToRead = _fat->mbr->fat_size16;
  unsigned int StartSect     = 2 /* FIXME: _fat->mbr->reserved + 1 */;

  floppyread(StartSect, SectorsToRead, _fat->fat);

  return;
}

/**
 *  fat_save()
 *  ulozi FAT tabulku z globalniho pole <fat> na disk
 *  IN:   [void]
 *  OUT:  [void]
 */
void fat_save(void) {
  unsigned int SectorsToWrite = _fat->mbr->fat_size16;
  unsigned int StartSect      = 2 /*_fat->mbr->reserved + 1*/;
  floppywrite(StartSect, SectorsToWrite, _fat->fat);

  _fat->changed = true;

  return;
}

/**
 *  fat_get_filename()
 *  vytvori nazev kompatibilni s FAT z nazvu souboru <src>
 *  a vrati ho v <dest>
 *  IN:   <src>   - nazev souboru ve formatu 8.3
 *        <dest>  - navratova hodnota
 *  OUT:  <dest>
 */
char far *fat_get_filename(const char far *src, char far *dest) {
  unsigned int i    = 0;
  unsigned int dot  = 0;

  /* nazev */
  while(src[i] != '.' && src[i] != '\0') {
    dest[i] = toupper(src[i]);
    i++;
  }
  dot = i;

  /* doplneni mezer do jmena */
  if(i < 8)
    while(i != 8)
      dest[i++] = ' ';

  /* pripona */
  while(src[dot++]) {
    dest[i] = toupper(src[dot]);
    i++;
  }

  /* doplneni mezer do pripony */
  if(i < 11)
    while(i != 11)
      dest[i++] = ' ';

  /* zakonceni retezce */
  dest[11] = '\0';

  return dest;
}

/**
 *  fat_get_real_filename()
 *  vytvori nazev souboru s teckou a bez mezer z <name> a <ext>
 *  a vrati ho v <realname>
 *  IN:   <name>    - jmeno souboru
 *        <ext>     - pripona souboru
 *        <realname>- zde bude navratova hodnota
 *  OUT:  <realname>
 */
char far *fat_get_real_filename(const char far *name, const char far *ext, char far *realname) {
  unsigned int i = 0;

  while((*name != ' ') && (*name != '\0') && (i++ != 8))
    *realname++ = tolower(*name++);

  if(!(*(ext) == ' ' && *(ext + 1) == ' ' && *(ext + 2) == ' ')) {
    *realname++ = '.';
    while((*ext != ' ') && (*ext != '\0') && (i++ != 11))
      *realname++ = tolower(*ext++);
  }

  *realname = '\0';

  return realname;
}

FatBackupData far *fat_dirs_begin(char far * far *filename) {
  FatBackupData far *fbd = mem_alloc(sizeof(FatBackupData), KERNEL_SEGMENT);
  fbd->actualDirBackup   = mem_alloc(64, KERNEL_SEGMENT);
  fbd->filenameLowercase = mem_alloc(strlen(*filename)+1, KERNEL_SEGMENT);

  /* zalohujeme si aktualni pracovni slozku */
  strcpy(fbd->actualDirBackup, _dir_actual->string);

  /* do <filenameLowercase> si zapiseme nazev souboru malymi pismeny */
  strtolower(*filename, fbd->filenameLowercase);

  if(pos(fbd->filenameLowercase, '/') != POS_NOT_FOUND) {               /* pokud je v souboru alespon jedno lomitko */
    fbd->filenameLowercase[l_pos(fbd->filenameLowercase, '/')] = '\0';  /* bude se jednat o cestu, za poslednim lomitkem zavreme string (zbyde nam jen string s nazvem souboru) */
    if(!strlen(fbd->filenameLowercase))
      change_dir("/");
    else
      change_dir(fbd->filenameLowercase);                               /* skocime do slozky */
    fbd->filenameLowercase += strlen(fbd->filenameLowercase)+1;         /* zmenime pointer jen na nazev souboru */
  }

  *filename = fbd->filenameLowercase;                                   /* prepiseme pointer */

  return fbd;
}

void fat_dirs_end(FatBackupData far *fbd) {
  change_dir(fbd->actualDirBackup);
  mem_free(fbd->filenameLowercase);
  mem_free(fbd->actualDirBackup);
  mem_free(fbd);
  return;
}

#if 0
/**
 *  fat_read_entry()
 *  precte z FAT <n>ty cluster a vrati jeho hodnotu
 *  IN: <n> - cluster
 *  OUT: <cluster> - hodnota
 */
unsigned int fat_read_entry(unsigned int n) {
  unsigned int cluster;
  cluster = *((unsigned int far *)(_fat->fat+((n * 3) / 2)));
  if (n & 1)
    cluster >>= 4;
  else
    cluster &= 0x0fff;
  return cluster;
}
#endif

/**
 *  fat_write_entry()
 *  zapise/prepise do FAT (v RAM) <n>ty cluster s hodnotou
 *  IN: <n> - cluster, <cluster> - hodnota
 *  OUT: [void]
 */
void fat_write_entry(unsigned int n, unsigned int cluster) {
  if (n%2 == 0) {
    *(_fat->fat+((n * 3) / 2))        = ( ((char *) &cluster)[0] );
    *(_fat->fat+((n * 3) / 2) + 1)   &= 0xF0;
    *(_fat->fat+((n * 3) / 2) + 1)   |= ( ((char *) &cluster)[1] & 0x0F );
  } else {
    *(_fat->fat+((n * 3) / 2))       &= 0x0F;
    *(_fat->fat+((n * 3) / 2))       |= ( ((((char *) &cluster)[0] & 0xFF) << 4) );

    *(_fat->fat+((n * 3) / 2) + 1)    = ( ((((char *) &cluster)[1] & 0xFF) << 4) );
    *(_fat->fat+((n * 3) / 2) + 1)   |= ( ((((char *) &cluster)[0] & 0xFF) >> 4) );
  }
  return;
}

/**
 *  fat_get_next_cluster()
 *  funkce vrati pristi cluster navazany na <cluster>
 *  IN:   <cluster>     - cislo clusteru
 *  OUT:  [int] cluster - cislo pristiho clusteru
 */
unsigned int fat_get_next_cluster(unsigned int far *cluster) {
  unsigned int t = *cluster;
  *cluster = *((unsigned int far *)(_fat->fat+((*cluster * 3) / 2)));
  if (t & 1)
    *cluster >>= 4;
  else
    *cluster &= 0x0fff;

  return *cluster;
}

/**
 *  fat_get_free_cluster()
 *  zjisti z FAT nejblizsi volny cluster a vrati jeho pozici
 *  IN:   [void]
 *  OUT:  [int] pozice ve FAT
 */
unsigned int fat_get_free_cluster(void) {
  unsigned int cluster      = 1;
  unsigned int t            = cluster;
  unsigned int i            = 0;
  unsigned int bytes        = _fat->sectors*512;

  for(i = 0; i <= bytes; i++) {
    cluster = *((unsigned int far *)(_fat->fat+((i * 3) / 2)));
    if (t & 1)
      cluster >>= 4;
    else
      cluster &= 0x0fff;
    t = cluster;
    if(!cluster)
      return i;
  }

  return 0;
}

